package cn.kivensoft.util;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.BiConsumer;


/** 简单的实现LRU缓存的字典类，具备超时和同步功能，创建时可选
 * @author Kiven Lee
 * @version 3.0.0
 * 3.0.0 修改内部实现代码，优化速度
 * 2.0.0 增加过期时间判断，优化并发访问性能
 * 1.0.0 简单实现LRU缓存
 */
final public class LruCache<K, V> implements Cache<K, V> {

	private final HashMap<K, Node<K, V>> data;
	private final int capacity;
	private final long expire;
	private final Lock lock;
	private final Node<K, V> list;

	/** 缺省构建函数, 默认不限制大小, 没有过期时间, 非线程安全 */
	public LruCache() {
		this(0, 0, false);
	}

	/**
	 * @param capacity 缓存容量
	 */
	public LruCache(int capacity) {
		this(capacity, 0, false);
	}

	/**
	 * @param capacity 缓存容量
	 * @param expire 过期时间
	 */
	public LruCache(int capacity, long expire) {
		this(capacity, expire, false);
	}

	/**
	 * @param capacity 缓存容量
	 * @param expire 过期时间
	 * @param isSynchronlzed 线程安全标志
	 */
	public LruCache(int capacity, long expire, boolean isSynchronlzed) {
		this.capacity = capacity;
		this.expire = expire;
		this.lock = isSynchronlzed ? new ReentrantLock() : null;
		this.data = new HashMap<>();
		this.list = new Node<>(null, null, 0);
		this.list.prev = list;
		this.list.next = list;
	}

	// 删除链表节点
	private void removeNode(Node<K, V> node) {
		node.prev.next = node.next;
		node.next.prev = node.prev;
	}

	// 把节点加入到链表末尾
	private void appendNode(Node<K, V> node) {
		node.prev = list;
		node.next = list.next;
		list.next.prev = node;
		list.next = node;
	}

	@Override
	public V get(K key) {
		if (lock == null)
			return _get(key);
		else {
			lock.lock();
			try {
				return _get(key);
			} finally {
				lock.unlock();
			}
		}
	}

	@Override
	public void put(K key, V value) {
		if (key == null || value == null)
			return;
		if (lock == null)
			_put(key, value);
		else {
			lock.lock();
			try {
				_put(key, value);
			} finally {
				lock.unlock();
			}
		}
	}

	@Override
	public void remove(K key) {
		if (lock == null)
			_remove(key);
		else {
			lock.lock();
			try {
				_remove(key);
			} finally {
				lock.unlock();
			}
		}
	}

	@Override
	public void clear() {
		if (lock == null)
			_clear();
		else {
			lock.lock();
			try {
				_clear();
			} finally {
				lock.unlock();
			}
		}
	}

	@Override
	public void cycle() {
		if (expire == 0)
			return;
		if (lock == null)
			_cycle();
		else {
			lock.lock();
			try {
				_cycle();
			} finally {
				lock.unlock();
			}
		}
	}

	@Override
	public int size() {
		return data.size();
	}

	@Override
	public boolean isEmpty() {
		return data.isEmpty();
	}

	@Override
	public int capacity() {
		return capacity;
	}

	@Override
	public boolean containsKey(K key) {
		if (lock == null)
			return data.containsKey(key);
		else {
			lock.lock();
			try {
				return data.containsKey(key);
			} finally {
				lock.unlock();
			}
		}
	}

	@Override
	public boolean isSynchronized() {
		return lock != null;
	}

	@Override
	public void forEach(BiConsumer<K, V> func) {
		if (lock == null)
			_forEach(func);
		else {
			lock.lock();
			try {
				_forEach(func);
			} finally {
				lock.unlock();
			}
		}
	}

	private V _get(K key) {
		Node<K, V> node = data.get(key);
		if (node == null) return null;
		if (expire > 0) {
			long now = System.currentTimeMillis();
			// 缓存项已过期, 删除缓存项及移除链表中的节点
			if (node.time + expire < now) {
				data.remove(key);
				removeNode(node);
				return null;
			}
			node.time = now;
		}
		// 如果设置容量大小或过期时间, 并且节点不在链表末尾, 则移动节点到链表末尾
		if ((capacity > 0 || expire > 0) && list.next != node) {
			removeNode(node);
			appendNode(node);
		}
		return node.value;
	}

	private void _put(K key, V value) {
		long now = expire > 0 ? System.currentTimeMillis() : 0;
		Node<K, V> node = new Node<>(key, value, now);
		Node<K, V> old = data.put(key, node);
		if (expire <= 0 && capacity <= 0) return;

		// 处理链表, 删除旧节点与把新节点加到末尾
		if (old != null) removeNode(old);
		appendNode(node);
		_cycle();
		if (capacity <= 0 || capacity >= data.size()) return;
		// 容量不足, 需要删除链表头部节点
		old = list.prev;
		data.remove(old.key);
		removeNode(old);
	}

	private void _remove(K key) {
		Node<K, V> node = data.remove(key);
		if (node != null && expire > 0) removeNode(node);
	}

	private void _clear() {
		data.clear();
		list.prev = list;
		list.next = list;
	}

	private void _cycle() {
		if (expire <= 0) return;
		long exp = System.currentTimeMillis() - expire;
		for (Node<K, V> node = list.prev; node != list; node = node.prev) {
			if (node.time < exp) {
				data.remove(node.key);
				removeNode(node);
			}
		}
	}

	private void _forEach(BiConsumer<K, V> func) {
		for (Map.Entry<K, Node<K, V>> entry : data.entrySet())
			func.accept(entry.getKey(), entry.getValue().value);
	}



	private static class Node<K, V> {
		final K key;
		final V value;
		long time;
		Node<K, V> prev, next;

		Node(K key, V value, long time) {
			this.key = key;
			this.value = value;
			this.time = time;
		}
	}

}
